using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Text;
using System.IO;
using System.Windows.Forms;
using GraphAlgsPresentation.Core;

namespace GraphAlgsPresentation.GraphEditor
{
    /// <summary>
    /// Represents a user control of GraphEditor
    /// </summary>
    public partial class GraphEditor : UserControl
    {
        private LayoutContext layoutContext;
        private LCVertex tempSourceVertex;
        private LCVertex tempVertex;
        private bool dragging;
        private DoubleBuffer db;

        #region Initialize

        /// <summary>
        /// Initializes a new instance of the <see cref="GraphEditor"/> class.
        /// </summary>
        public GraphEditor()
        {
            InitializeComponent();
            layoutContext = new LayoutContext();
            gp.LayoutContext = layoutContext;
            tempSourceVertex = new LCVertex();
            tempVertex = new LCVertex();
            dragging = false;
            db = new DoubleBuffer();
        }

        /// <summary>
        /// Handles the Load event of the GraphEditor control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void GraphEditor_Load(object sender, EventArgs e)
        {
            VertexMode = true;
            SelectedObject = LayoutContext;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the layout context.
        /// </summary>
        /// <value>The layout context.</value>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public LayoutContext LayoutContext
        {
            get { return layoutContext; }
            set { layoutContext = value; }
        }

        /// <summary>
        /// Gets or sets the selected object.
        /// </summary>
        /// <value>The selected object.</value>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Object SelectedObject
        {
            get
            {
                if (property.SelectedObject is LayoutContext) return null;
                return property.SelectedObject;
            }
            set { property.SelectedObject = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [select mode].
        /// </summary>
        /// <value><c>true</c> if [select mode]; otherwise, <c>false</c>.</value>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool SelectMode
        {
            get { return btnSelect.Checked; }
            set
            {
                btnSelect.Checked = value;
                if (value)
                    btnVertex.Checked = btnEdge.Checked = false;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [vertex mode].
        /// </summary>
        /// <value><c>true</c> if [vertex mode]; otherwise, <c>false</c>.</value>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool VertexMode
        {
            get { return btnVertex.Checked; }
            set
            {
                btnVertex.Checked = value;
                if (value)
                    btnSelect.Checked = btnEdge.Checked = false;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [edge mode].
        /// </summary>
        /// <value><c>true</c> if [edge mode]; otherwise, <c>false</c>.</value>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool EdgeMode
        {
            get { return btnEdge.Checked; }
            set
            {
                btnEdge.Checked = value;
                if (value)
                    btnVertex.Checked = btnSelect.Checked = false;
            }
        }

        #endregion

        #region Toolbar methods

        /// <summary>
        /// Handles the Click event of the btnSelect control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSelect_Click(object sender, EventArgs e)
        {
            SelectMode = true;
            SelectedObject = LayoutContext;
        }

        /// <summary>
        /// Handles the Click event of the btnVertex control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnVertex_Click(object sender, EventArgs e)
        {
            VertexMode = true;
            SelectedObject = LayoutContext;
        }

        /// <summary>
        /// Handles the Click event of the btnEdge control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnEdge_Click(object sender, EventArgs e)
        {
            EdgeMode = true;
            SelectedObject = LayoutContext;
        }

        /// <summary>
        /// Handles the Click event of the btnSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnSave_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveDialog = new SaveFileDialog();
            saveDialog.OverwritePrompt = true;
            saveDialog.DefaultExt = "xml";
            saveDialog.Filter = "XML Files | *.xml";
            if (saveDialog.ShowDialog() == DialogResult.OK)
            {
                this.Save(saveDialog.FileName);
            }
        }

        /// <summary>
        /// Saves the specified file name.
        /// </summary>
        /// <param name="FileName">Name of the file.</param>
        public void Save(string FileName)
        {
            StringBuilder s = new StringBuilder();
            LayoutContext.Serialize(new StringWriter(s));
            StreamWriter fo = new StreamWriter(FileName);
            fo.Write(s.ToString());
            fo.Close();
        }

        /// <summary>
        /// Handles the Click event of the btnLoad control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnLoad_Click(object sender, EventArgs e)
        {
            OpenFileDialog openDialog = new OpenFileDialog();
            openDialog.CheckPathExists = true;
            openDialog.DefaultExt = "xml";
            openDialog.Filter = "XML Files | *.xml";
            if (openDialog.ShowDialog() == DialogResult.OK)
            {
                StringBuilder s = new StringBuilder();
                StreamReader fi = new StreamReader(openDialog.FileName);
                s.Append(fi.ReadToEnd());
                fi.Close();
                LayoutContext = LayoutContext.Deserialize(new StringReader(s.ToString()));
                gp.LayoutContext = LayoutContext;
                gp.Draw();
                SelectMode = true;
                SelectedObject = LayoutContext;
            }
        }

        /// <summary>
        /// Handles the Click event of the btnDelete control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnDelete_Click(object sender, EventArgs e)
        {
            if (SelectedObject is LCEdge)
            {
                LayoutContext.Edges.Remove(SelectedObject as LCEdge);
                SelectedObject = LayoutContext;
                gp.Draw();
                return;
            }
            if (SelectedObject is LCVertex)
            {
                LCVertex vertex = (LCVertex)SelectedObject;
                bool stop = false;
                while (!stop)
                {
                    stop = true;
                    foreach (LCEdge edge in LayoutContext.Edges)
                        if (edge.Contains(vertex))
                        {
                            LayoutContext.Edges.Remove(edge);
                            stop = false;
                            break;
                        }
                }
                LayoutContext.Vertices.Remove(vertex);
                for (int i = 0; i < LayoutContext.Vertices.Count; i++)
                    LayoutContext.Vertices[i].ChangeName(((int)(i + 1)).ToString());
                SelectedObject = LayoutContext;
                gp.Draw();
                return;
            }
            MessageBox.Show("Please choose something to delete.");
        }

        /// <summary>
        /// Handles the Click event of the btnExit control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnExit_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        /// <summary>
        /// Handles the Click event of the btnShowWeight control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnShowWeight_Click(object sender, EventArgs e)
        {
            foreach (LCEdge edge in this.layoutContext.Edges)
                edge.Label = "{0}";
            gp.Draw();
        }

        /// <summary>
        /// Handles the Click event of the btnHideWeight control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnHideWeight_Click(object sender, EventArgs e)
        {
            foreach (LCEdge edge in this.layoutContext.Edges)
                if (edge.Label == "{0}")
                    edge.Label = "";
            gp.Draw();
        }

        /// <summary>
        /// Handles the Click event of the btnNew control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void btnNew_Click(object sender, EventArgs e)
        {
            LayoutContext = new LayoutContext();
            gp.LayoutContext = LayoutContext;
            gp.Draw();
            SelectedObject = LayoutContext;
        }

        #endregion

        #region Mouse methods

        /// <summary>
        /// Handles the MouseClick event of the gp control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void gp_MouseClick(object sender, MouseEventArgs e)
        {
            if (SelectMode) MouseClickSelectMode(new Point(e.X, e.Y));
            if (VertexMode) MouseClickVertexMode(new Point(e.X, e.Y));
        }

        /// <summary>
        /// Handles the MouseDown event of the gp control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void gp_MouseDown(object sender, MouseEventArgs e)
        {
            if (SelectMode) MouseDownSelectMode(new Point(e.X, e.Y));
            if (EdgeMode) MouseDownEdgeMode(new Point(e.X, e.Y));
        }

        /// <summary>
        /// Handles the MouseUp event of the gp control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void gp_MouseUp(object sender, MouseEventArgs e)
        {
            if (EdgeMode) MouseUpEdgeMode(new Point(e.X, e.Y));
            dragging = false;
        }

        /// <summary>
        /// Handles the MouseMove event of the gp control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.MouseEventArgs"/> instance containing the event data.</param>
        private void gp_MouseMove(object sender, MouseEventArgs e)
        {
            if (SelectMode) MouseMoveSelectMode(new Point(e.X, e.Y));
            if (EdgeMode) MouseMoveEdgeMode(new Point(e.X, e.Y));
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Handles the PropertyValueChanged event of the property control.
        /// </summary>
        /// <param name="s">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.PropertyValueChangedEventArgs"/> instance containing the event data.</param>
        private void property_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
        {
            gp.Draw();
        }

        /// <summary>
        /// Handles the SelectedObjectsChanged event of the property control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void property_SelectedObjectsChanged(object sender, EventArgs e)
        {
            foreach (LCVertex vertex in LayoutContext.Vertices)
                vertex.Marked = false;
            foreach (LCEdge edge in LayoutContext.Edges)
                edge.Marked = false;
            if (SelectedObject is LCVertex) (SelectedObject as LCVertex).Marked = true;
            if (SelectedObject is LCEdge) (SelectedObject as LCEdge).Marked = true;
            gp.Draw();
        }

        #endregion

        #region Select mode

        /// <summary>
        /// Mouse click when select mode.
        /// </summary>
        /// <param name="point">The point.</param>
        private void MouseClickSelectMode(Point point)
        {
            Object obj = LayoutContext.GetObject(point.X, point.Y);
            if (obj == null) obj = LayoutContext;
            SelectedObject = obj;
        }

        /// <summary>
        /// Mouse down when select mode.
        /// </summary>
        /// <param name="point">The point.</param>
        private void MouseDownSelectMode(Point point)
        {
            Object obj = LayoutContext.GetObject(point.X, point.Y);
            if (obj is LCVertex)
            {
                LCVertex vertex = (LCVertex)obj;
                SelectedObject = vertex;
                tempVertex.Position = vertex.Position;
                tempVertex.Radius = vertex.Radius;
                dragging = true;
            }
        }

        /// <summary>
        /// Mouse move when select mode.
        /// </summary>
        /// <param name="point">The point.</param>
        private void MouseMoveSelectMode(Point point)
        {
            Object obj = LayoutContext.GetObject(point.X, point.Y);
            if (obj == null) Cursor = Cursors.Default;
            else Cursor = Cursors.SizeAll;
            if (dragging)
            {
                if (!tempVertex.CheckInside(point.X, point.Y))
                {
                    LCVertex vertex = (LCVertex)SelectedObject;
                    vertex.Position = point;
                    gp.Draw();
                }
            }
        }

        #endregion

        #region Vertex mode

        /// <summary>
        /// Mouse click when vertex mode.
        /// </summary>
        /// <param name="point">The point.</param>
        private void MouseClickVertexMode(Point point)
        {
            Object obj = LayoutContext.GetObject(point.X, point.Y);
            if (obj == null)
            {
                LCVertex vertex = new LCVertex(point, LayoutContext.GetNextName());
                LayoutContext.Vertices.Add(vertex);
                gp.Draw();
                SelectedObject = vertex;
            }
        }

        #endregion

        #region Edge mode

        /// <summary>
        /// Mouse down when edge mode.
        /// </summary>
        /// <param name="point">The point.</param>
        private void MouseDownEdgeMode(Point point)
        {
            SelectedObject = LayoutContext;
            Object obj = LayoutContext.GetObject(point.X, point.Y);            
            if (obj is LCVertex)
            {
                tempSourceVertex = (LCVertex)obj;
                dragging = true;
                db.CreateOriginalBackground(gp.Width, gp.Height);
                db.ResetBufferBackground();
                Graphics g = db.GetBufferBackgroundGraphics();
                LayoutContext.Draw(g);
                g.Dispose();
            }
        }

        /// <summary>
        /// Mouse move when edge mode.
        /// </summary>
        /// <param name="point">The point.</param>
        private void MouseMoveEdgeMode(Point point)
        {
            Object obj = LayoutContext.GetObject(point.X, point.Y);
            if (obj == null) Cursor = Cursors.Default;
            else Cursor = Cursors.SizeAll;
            if ((dragging) && (!tempSourceVertex.CheckInside(point.X, point.Y)))
            {
                db.ResetDoubleBufferBackground();
                Graphics g = db.GetDoubleBufferBackgroundGraphics();
                Pen pen = new Pen(Color.Black, 3);
                pen.DashStyle = DashStyle.Dash;
                if (obj is LCVertex)
                {
                    g.DrawLine(pen,
                        tempSourceVertex.GetIntersectantPoint((obj as LCVertex).Position),
                        (obj as LCVertex).GetIntersectantPoint(tempSourceVertex.Position));
                }
                else
                {
                    g.DrawLine(pen,
                        tempSourceVertex.GetIntersectantPoint(point),
                        point);
                }
                pen.Dispose();
                g.Dispose();
                g = gp.CreateGraphics();
                db.DrawImage(g, db.GetDoubleBufferBackgroundBitmap());
                g.Dispose();
            }
        }

        /// <summary>
        /// Mouse up when edge mode.
        /// </summary>
        /// <param name="point">The point.</param>
        private void MouseUpEdgeMode(Point point)
        {
            Object obj = LayoutContext.GetObject(point.X, point.Y);
            if ((obj is LCVertex) && (obj != tempSourceVertex) &&
                (LayoutContext.GetEdge(tempSourceVertex.Name, (obj as LCVertex).Name) == null))
            {
                LCEdge edge = new LCEdge();
                edge.Source = tempSourceVertex;
                edge.Destination = (LCVertex)obj;
                LayoutContext.Edges.Add(edge);                
                SelectedObject = edge;
            }
            else SelectedObject = LayoutContext;
            gp.Draw();
        }

        #endregion
    }
}
